#include "drawingarea.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constructors

DrawingArea::DrawingArea(PImage* pimg) : listLayers(){
    pimage = pimg;
    this->setBackgroundBrush(*(new QBrush(*(new QColor(100, 100, 100)))));

    width = 0;
    height = 0;

    listLayers.clear();
    currentLayer = 0;

    controlPressed =false;

    currentTool = Selection_Rect;
    selection = NULL;
    cropping = NULL;
    pencil = NULL;

    borderLayer = new BorderLayer();

    semaphore = new QSemaphore(1);

    // we are only interessed of the movement of the mouse when the use mouse is pressed
    viewport()->setMouseTracking(false);

    setUpdatesEnabled(true);
    margeSceneX = 100;
    margeSceneY = 100;

    scaleXY = 1;

    XBeginSelection = 0;
    XEndSelection = 0;
    YBeginSelection = 0;
    YEndSelection=0;
}

DrawingArea::DrawingArea(PImage* pimg, int width, int height){
    pimage = pimg;
    this->setBackgroundBrush(*(new QBrush(*(new QColor(100, 100, 100)))));

    listLayers.clear();
    currentLayer = 0;

    currentTool = Selection_Rect;
    selection = NULL;
    cropping = NULL;
    pencil = NULL;

    borderLayer = new BorderLayer();

    controlPressed =false;

    semaphore = new QSemaphore(1);

    // we are only interessed of the movement of the mouse when the use mouse is pressed
    viewport()->setMouseTracking(false);

    setUpdatesEnabled(true);
    margeSceneX = 100;
    margeSceneY = 100;

    scaleXY = 1;

    // creation of the new picture compose with the unique layer load
    Layer* layer = new Layer(0, 0, width, height, width, height);
    this->width = width;
    this->height = height;

    setScene(new QGraphicsScene(-margeSceneX, -margeSceneY,width + 2 * margeSceneX,height + 2 * margeSceneY, this));

    listLayers.push_back(layer);

    layer->setPos(0, 0);
    // the image will be positionned on the background of the scene
    layer->setZValue(0);

    scene()->addItem(layer);

    borderLayer->setZValue(1);
    setCurrentLayer(layer);
    scene()->addItem(borderLayer);
    background = new Background(this, width, height);
    scene()->addItem(background);

    XBeginSelection = 0;
    XEndSelection = 0;
    YBeginSelection = 0;
    YEndSelection=0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Load and save

void DrawingArea::load(const QString fileName){
    // cleaning of the previous existing layers
    if(scene() != NULL){
        while(!listLayers.empty()){
            Layer* layer = listLayers.front();
            listLayers.pop_front();
            scene()->removeItem(layer);
            delete layer;
        }
    }
    listLayers.clear();

    // creation of the new picture compose with the unique layer load
    Layer* layer = new Layer(fileName);
    width = layer->getWidth();
    height = layer->getHeight();

    setScene(new QGraphicsScene(-margeSceneX, -margeSceneY,
                                layer->getWidth() + 2 * margeSceneX,
                                layer->getHeight() + 2 * margeSceneY, this));

    listLayers.push_back(layer);

    layer->setPos(0, 0);
    // the image will be positionned on the background of the scene
    layer->setZValue(0);

    if (layer->getWidth()>800 || layer->getHeight()>600){
        for(int i=0;i<10;i++){
             scale(qreal(1.0 / 1.1), qreal(1.0 / 1.1));
        }

        pimage->afficheZoom(this);
        
    }
    scene()->addItem(layer);

    borderLayer->setZValue(1);
    setCurrentLayer(layer);
    scene()->addItem(borderLayer);

    background = new Background(this, width, height);
    scene()->addItem(background);
}

void DrawingArea::save(const QString fileName){
    list <Layer*> tmpListLayer = getLayers();
    int nbLayers = tmpListLayer.size();
    for(int i = 0; i < nbLayers; i++){
        Layer* tmpLayer = tmpListLayer.back();
        tmpListLayer.pop_back();
        if(tmpLayer->isVisible()){
            tmpListLayer.push_front(tmpLayer);
        }
    }

    FusionLayers* fusion = new FusionLayers(SizeImage, getWidth(), getHeight());
    Layer* fusionLayer = fusion->fusion(tmpListLayer);
    fusionLayer->getImage()->save(fileName);

    delete fusionLayer;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Render and Resize

QImage DrawingArea::render(){
    QImage imageRender(width, height, listLayers.front()->getImage()->format());

    QPainter* painter = new QPainter(&imageRender);

    list <Layer*> tmpListLayer = listLayers;

    while(!tmpListLayer.empty()){
        tmpListLayer.front()->paint(painter, NULL, NULL);
        tmpListLayer.pop_front();
    }
    delete painter;
    return imageRender;
}

void DrawingArea::resize(int newWidth, int newHeight){
    list <Layer*> tmpListLayer;
    // Remove the element of the scene
    if(selection != NULL){
        scene()->removeItem(selection);
    }
    if(cropping != NULL){
        scene()->removeItem(cropping);
    }
    if(pencil != NULL){
        scene()->removeItem(pencil);
    }
    scene()->removeItem(borderLayer);
    scene()->removeItem(background);
    tmpListLayer = getLayers();
    while(!tmpListLayer.empty()){
        scene()->removeItem(tmpListLayer.back());
        tmpListLayer.pop_back();
    }
    delete scene();
    // Update of the new dimension
    width = newWidth;
    height = newHeight;
    // Creation of a new scene
    setScene(new QGraphicsScene(-margeSceneX, -margeSceneY, width + 2 * margeSceneX, height + 2 * margeSceneY, this));
    ensureVisible(0, 0, width, height);
    if(selection != NULL){
        scene()->addItem(selection);
        selection->refresh();
    }
    if(cropping != NULL){
        scene()->addItem(cropping);
    }
    if(pencil != NULL){
        scene()->addItem(pencil);
    }
    scene()->addItem(borderLayer);
    scene()->addItem(background);
    tmpListLayer.clear();
    tmpListLayer = getLayers();
    while(!tmpListLayer.empty()){
        scene()->addItem(tmpListLayer.back());
        tmpListLayer.pop_back();
    }
    borderLayer->refreshPos();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Filter

void DrawingArea::applyFilter(Filter* filter){
    getCurrentLayer()->applyFilter(filter);
    update();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Key event

void DrawingArea::keyPressEvent(QKeyEvent* key){
    if (key->key()==Qt::Key_Control){
         controlPressed =true;
     }
}

void DrawingArea::keyReleaseEvent(QKeyEvent* key){
     if (key->key()==Qt::Key_Control){
         controlPressed =false;
     }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mouse event

void DrawingArea::wheelEvent (QWheelEvent* e) {
    if(controlPressed == true){
        if (e->delta()>0){
           pimage->zoomPlus();
        }else{
           pimage->zoomMoin();
        }
    }
    QGraphicsView::wheelEvent(e);
}

void DrawingArea::mousePressEvent (QMouseEvent* event ){
    QPointF coordonnee = this->mapToScene(event->x(), event->y());
    switch (currentTool)    {
        case Cropped:
            cropping->beginSelectionRect((int) coordonnee.x(), (int) coordonnee.y());
            // we show the effect of the tool over all the other layers
            cropping->setZValue(getLayersCount() + 1);
            break;
        case Take_Color:
            Layer* layer;
            layer = getCurrentLayer();
            takeColor->recupColor(coordonnee, layer,pimage);
            break;
        case Moved_Tool:
            mover->initMove((int) coordonnee.x(), (int) coordonnee.y());
            break;
        case MovedSelection_Tool:
            moverSelection->initMoveSelection((int) coordonnee.x(), (int) coordonnee.y());
            break;
        case Selection_Rect:
            ((SelectionRect*) selection)->beginSelectionRect((int) coordonnee.x(),(int) coordonnee.y());
             selection->setZValue(getLayersCount() + 1);
            break;
        case Clever_Selection:
            break;
        case Pencil_Tool :
            pencil->initPencil((int) coordonnee.x(),(int) coordonnee.y());
            pencil->designer((int) coordonnee.x(),(int) coordonnee.y());
            update();
            break;
    }
}

void DrawingArea::mouseMoveEvent(QMouseEvent * event ){
    QPointF coordonnee = this->mapToScene(event->x(), event->y());
    switch (currentTool)    {
        case Cropped:
            cropping->selectionningRect(coordonnee.x(), coordonnee.y());
            update();
            break;
        case Take_Color:
            break;
        case Moved_Tool:
            mover->moving(coordonnee.x(), coordonnee.y());
            borderLayer->refreshPos();
            update();
            break;
        case MovedSelection_Tool:
            moverSelection->movingSelection(coordonnee.x(), coordonnee.y());
            update();
            break;
        case Selection_Rect:
            ((SelectionRect*) selection)->selectionningRect(coordonnee.x(), coordonnee.y());
            update();
            break;
        case Clever_Selection:
            break;
        case Pencil_Tool:
           pencil->designer((int) coordonnee.x(),(int) coordonnee.y());
           update();
           break;
    }
}

void DrawingArea::mouseReleaseEvent(QMouseEvent * event){
    QPointF coordonnee = this->mapToScene(event->x(), event->y());
    switch (currentTool)    {
        case Cropped:
            cropping->endSelectionRect((int) coordonnee.x(), (int) coordonnee.y());
            update();
            break;
        case Take_Color:
            break;
        case Moved_Tool:
            borderLayer->refreshPos();
            update();
            break;
        case MovedSelection_Tool:
            update();
            break;
        case Selection_Rect:
            ((SelectionRect*) selection)->endSelectionRect((int) coordonnee.x(), (int) coordonnee.y());
            update();
            break;
        case Clever_Selection:
            break;
        case Pencil_Tool:
           pencil->endPainting();
           update();
           break;
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tools

void DrawingArea::setTool(e_tool newSelectTool, QDockWidget* dockInfoTool){
    switch (currentTool)    {
        case Cropped:
            if(cropping != NULL){
                scene()->removeItem((QGraphicsItem*) cropping);
                delete cropping;
                cropping = NULL;
            }
            break;
        case Take_Color:
            if(takeColor != NULL){
                delete takeColor;
            }
            break;
        case Moved_Tool:
            if(mover != NULL){
                scene()->removeItem(borderLayer);
                delete mover;
                mover = NULL;
            }
            break;
        case MovedSelection_Tool:
            if(moverSelection != NULL){
                moverSelection->endMoverSelection();
                update();
                scene()->removeItem((QGraphicsItem*) moverSelection);
                delete moverSelection;
                moverSelection = NULL;
            }
            break;
        case Selection_Rect:
            if(selection != NULL){
                scene()->removeItem((QGraphicsItem*) selection);
                delete selection;
                selection = NULL;
            }
            break;
        case Clever_Selection:
            break;
        case Pencil_Tool:
            if (pencil != NULL){
                scene()->removeItem((QGraphicsItem*) pencil);
                delete pencil;
                pencil = NULL;
            }
            break;

    }
    currentTool = newSelectTool;
    //changement of the cursor
    this->setCursor(cursorTool(newSelectTool));

    switch (newSelectTool){
        case Cropped:
            cropping = new Cropping(this,dockInfoTool);
            scene()->addItem((QGraphicsItem*) cropping);
            cropping->createAffichage();
            break;
        case Take_Color:
            takeColor = new TakeColor(this,dockInfoTool);
            takeColor->createAffichage();
            break;
        case Moved_Tool:
            mover = new Mover(this,dockInfoTool);
            borderLayer->setLayer(getCurrentLayer());
            scene()->addItem(borderLayer);
            mover->createAffichage();
            break;
        case MovedSelection_Tool:
            moverSelection = new MoverSelection(this,dockInfoTool,XBeginSelection,XEndSelection,YBeginSelection,YEndSelection);
            scene()->addItem((QGraphicsItem*) moverSelection);
            moverSelection->createAffichage();
            break;
        case Selection_Rect:
            selection = new SelectionRect(this,dockInfoTool);
            scene()->addItem((QGraphicsItem*) selection);
            break;
        case Clever_Selection:
            break;
        case Pencil_Tool:
            pencil = new Pencil(this,dockInfoTool);
            scene()->addItem((QGraphicsItem*) pencil);
            pencil->createAffichage();
            break;
    }
}

QCursor DrawingArea::cursorTool(e_tool tool){
    switch (tool){
        case Cropped:
            return QCursor(Qt::CrossCursor);
            break;
        case Take_Color:
            return QCursor(Qt::WhatsThisCursor);
            break;
        case Moved_Tool:
            return QCursor(Qt::OpenHandCursor);
            break;
        case MovedSelection_Tool:
            return QCursor(Qt::SizeAllCursor);
            break;
        case Selection_Rect:
            return QCursor(Qt::CrossCursor);
            break;
        case Clever_Selection:
            return QCursor(Qt::ArrowCursor);
            break;
        case Pencil_Tool:
            return QCursor(Qt::PointingHandCursor);
            break;
        default :
            return QCursor(Qt::ArrowCursor);
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Function Select ALL

void DrawingArea::selectAll(QDockWidget* dockInfoTool){
     if(selection == NULL){
        selection = new SelectionRect(this,dockInfoTool);
        ((SelectionRect*) selection)->beginSelectionRect(0,0);
        ((SelectionRect*) selection)->endSelectionRect(width,height);
        selection->setZValue(getLayersCount());
        scene()->addItem((QGraphicsItem*) selection);
     }else{
        // Deselection of the previous selection
        scene()->removeItem((QGraphicsItem*) selection);
        delete selection;
        selection = NULL;
        // creation of a new selection
        selection = new SelectionRect(this,dockInfoTool);
        ((SelectionRect*) selection)->beginSelectionRect(0,0);
        ((SelectionRect*) selection)->endSelectionRect(width,height);
        selection->setZValue(getLayersCount());
        scene()->addItem((QGraphicsItem*) selection);
    }
   update();
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rotation

void DrawingArea::rotate(qreal angle){
    // the fonction do only the rotation multiple of 90°
    int tmpWidth = width;
    width = height;
    height = tmpWidth;
    list <Layer*> tmpListLayer = listLayers;
    while (!tmpListLayer.empty()){
        tmpListLayer.front()->rotate(angle);
        tmpListLayer.front()->setSizeBoundingRect(width, height);
        tmpListLayer.pop_front();
    }
    resize(width, height);
    borderLayer->refreshPos();
    update();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mirors

void DrawingArea::MiroirVertical(){
    list <Layer*> tmpListLayer = listLayers;
    while (!tmpListLayer.empty()){
        tmpListLayer.front()->MiroirVertical();
        tmpListLayer.front()->setSizeBoundingRect(width, height);
        tmpListLayer.pop_front();
    }
    update();
}

void DrawingArea::MiroirHorizontal(){
    list <Layer*> tmpListLayer = listLayers;
    while (!tmpListLayer.empty())    {
        tmpListLayer.front()->MiroirHorizontal();
        tmpListLayer.front()->setSizeBoundingRect(width, height);
        tmpListLayer.pop_front();
    }
    update();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copy & Paste

Layer* DrawingArea::copy(){
    Layer* imageSelected;
    if(selection == NULL || selection->getIsDeselectionned())    {
        imageSelected = getCurrentLayer()->copy();
    }else{
        imageSelected = selection->imageSelected(getCurrentLayer(), getCurrentLayer()->x(), getCurrentLayer()->y());
    }
    return imageSelected;
}

void DrawingArea::paste(Layer* layer){
    // Add the selection to the existing layer of drawing area
    layer->setSizeBoundingRect(getWidth(), getHeight());
    layer->setPos(0, 0);
    addLayerAfterCurrentLayer(layer);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Operation on the layers

void DrawingArea::addLayerBeforeCurrentLayer(){
    Layer* emptyLayer = new Layer(0, 0, width, height, width, height);
    emptyLayer->setSizeBoundingRect(width, height);
    addLayerBeforeCurrentLayer(emptyLayer);
}

void DrawingArea::addLayerAfterCurrentLayer(){
    Layer* emptyLayer = new Layer(0, 0, width, height, width, height);
    emptyLayer->setSizeBoundingRect(width, height);
    addLayerAfterCurrentLayer(emptyLayer);
}

void DrawingArea::addLayerAfterCurrentLayer(Layer* layer){
    list <Layer*> tmpListLayer;
    tmpListLayer.clear();
    Layer* tmpLayer;
    if(listLayers.size() == 0){
        scene()->addItem(borderLayer);
    }
    // We pop the layer of the list of layer to find the current layer
    if(!listLayers.empty()){
        tmpLayer = listLayers.back();
        while(tmpLayer != currentLayer && !listLayers.empty()){
            tmpListLayer.push_back(tmpLayer);
            listLayers.pop_back();
            tmpLayer = listLayers.back();
        }
        // We pop the current layer
        tmpListLayer.push_back(tmpLayer);
        listLayers.pop_back();
    }
    // We insert the new layer
    tmpListLayer.push_back(layer);
    scene()->addItem(layer);
    // The layer will be set as the current
    setCurrentLayer(layer);
    // We continu to pop the list of layer to have a compete temporary list of layer
    while(!listLayers.empty()){
        tmpListLayer.push_back(listLayers.back());
        listLayers.pop_back();
    }
    // We reconstructe the list of layers and rewriting the z value (corresponding
    // To the order of painting or the layer)
    int zValue = 0;
    while(!tmpListLayer.empty()){
        tmpLayer = tmpListLayer.front();
        tmpListLayer.pop_front();
        tmpLayer->setZValue(zValue);
        zValue++;
        listLayers.push_front(tmpLayer);
    }
    borderLayer->setZValue(zValue);
    zValue++;
    if(selection != NULL){
        selection->setZValue(zValue);
        zValue++;
    }
    if(cropping != NULL){
        cropping->setZValue(zValue);
        zValue++;
    }
    // Refresh the tool
    if(selection != NULL){
        selection->refresh();
    }
    update();
}

void DrawingArea::addLayerBeforeCurrentLayer(Layer* layer){
    list <Layer*> tmpListLayer;
    tmpListLayer.clear();
    Layer* tmpLayer;
    // We pop the layer of the list of layer to find the current layer
    if(!listLayers.empty()){
        tmpLayer = listLayers.back();
        while(tmpLayer != currentLayer && !listLayers.empty()){
            tmpListLayer.push_back(tmpLayer);
            listLayers.pop_back();
            tmpLayer = listLayers.back();
        }
    }
    // We insert the new layer
    tmpListLayer.push_back(layer);
    scene()->addItem(layer);
    setCurrentLayer(layer);
    // We continu to pop the list of layer to have a compete temporary list of layer
    while(!listLayers.empty()){
        tmpListLayer.push_back(listLayers.back());
        listLayers.pop_back();
    }
    // We reconstructe the list of layers and rewriting the z value (corresponding
    // To the order of painting or the layer)
    int zValue = 0;
    while(!tmpListLayer.empty()){
        Layer* tmpLayer = tmpListLayer.front();
        tmpListLayer.pop_front();
        tmpLayer->setZValue(zValue);
        zValue++;
        listLayers.push_front(tmpLayer);
    }
    borderLayer->setZValue(zValue);
    zValue++;
    if(selection != NULL){
        selection->setZValue(zValue);
        zValue++;
    }
    if(cropping != NULL){
        cropping->setZValue(zValue);
        zValue++;
    }
    // Refresh the tool
    if(selection != NULL){
        selection->refresh();
    }
    update();
}

void DrawingArea::removeCurrentLayer(){
    Layer* layer = getCurrentLayer();
    if(layer!=NULL){
        list <Layer*> tmpListLayer;
        tmpListLayer.clear();
        Layer* tmpLayer;
        // We pop the layer of the list of layer to find the layer to delete
        if(!listLayers.empty()){
            tmpLayer = listLayers.back();
            while(tmpLayer != layer && !listLayers.empty()){
                tmpListLayer.push_back(tmpLayer);
                listLayers.pop_back();
                tmpLayer = listLayers.back();
            }
        }
        // We delete the layer
        listLayers.pop_back();
        scene()->removeItem(tmpLayer);
        delete tmpLayer;

        if(!tmpListLayer.empty()){
            setCurrentLayer(tmpListLayer.back());
        }else if(!listLayers.empty()){
            setCurrentLayer(listLayers.back());
        }else if(listLayers.empty() && tmpListLayer.empty()){
            borderLayer->setLayer(NULL);
            scene()->removeItem(borderLayer);
            setCurrentLayer(NULL);
        }
        // We continu to pop the list of layer to have a compete temporary list of layer
        while(!tmpListLayer.empty()){
            listLayers.push_back(tmpListLayer.back());
            tmpListLayer.pop_back();
        }
        // Refresh the tool
        if(selection != NULL){
            selection->refresh();
        }
        update();
    }
}

void DrawingArea::removeLayer(Layer* layerToRemove){
    if(currentLayer != layerToRemove)    {
        Layer* futureCurrentLayer;
        futureCurrentLayer = currentLayer;
        setCurrentLayer(layerToRemove);
        removeCurrentLayer();
        setCurrentLayer(futureCurrentLayer);
    }else{
        setCurrentLayer(layerToRemove);
        removeCurrentLayer();
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Manage the current layer

void DrawingArea::selectTheLowerLayer(){
    Layer* currentLayer = getCurrentLayer();
    list <Layer*> tmpListLayer = getLayers();
    Layer* tmpLayer;
    Layer* tmpPreviousLayer;
    // We pop the layer of the list of layer to find the layer to delete
    if(!tmpListLayer.empty()){
        tmpLayer = tmpListLayer.back();
        tmpPreviousLayer = NULL;
        while(tmpLayer != currentLayer && !tmpListLayer.empty()){
            tmpListLayer.pop_back();
            tmpPreviousLayer = tmpLayer;
            tmpLayer = tmpListLayer.back();
        }
        if(tmpPreviousLayer != NULL){
            setCurrentLayer(tmpPreviousLayer);
        }
    }
    updateProprietyLayer();
    update();
}

void DrawingArea::selectTheUpperlayer(){
    Layer* currentLayer = getCurrentLayer();
    list <Layer*> tmpListLayer = getLayers();
    Layer* tmpLayer;
    // We pop the layer of the list of layer to find the layer to delete
    if(!tmpListLayer.empty()){
        tmpLayer = tmpListLayer.back();
        while(tmpLayer != currentLayer && !tmpListLayer.empty()){
            tmpListLayer.pop_back();
            tmpLayer = tmpListLayer.back();
        }
        tmpListLayer.pop_back();
        if(!tmpListLayer.empty()){
            setCurrentLayer(tmpListLayer.back());
        }
    }
    updateProprietyLayer();
    update();
}

void DrawingArea::selectTheLowestLayer(){
    setCurrentLayer(listLayers.back());
    updateProprietyLayer();
    update();
}

void DrawingArea::selectTheUppestLayer(){
    setCurrentLayer(listLayers.front());
    updateProprietyLayer();
    update();
}

void DrawingArea::takeDownTheLayer(){
    list <Layer*> tmpListLayer;
    tmpListLayer.clear();
    Layer* tmpLayer;
    Layer* tmpPreviousLayer;
    // We pop the layer of the list of layer to find the current layer
    if(!listLayers.empty()){
        tmpLayer = listLayers.back();
        tmpPreviousLayer = NULL;
        while(tmpLayer != currentLayer && !listLayers.empty()){
            tmpListLayer.push_back(tmpLayer);
            listLayers.pop_back();
            tmpPreviousLayer = tmpLayer;
            tmpLayer = listLayers.back();
        }
        if(tmpPreviousLayer != NULL){
            // Wwe pop the tmpLayer of the listLayers
            listLayers.pop_back();
            qreal zValueTmpLayer = tmpLayer->zValue();
            // Interchangement of the zvalue of the current layer and its previous layer
            tmpLayer->setZValue(tmpPreviousLayer->zValue());
            tmpPreviousLayer->setZValue(zValueTmpLayer);
            // Insertion of the previous layer and the current layer
            listLayers.push_back(tmpPreviousLayer);
            listLayers.push_back(tmpLayer);
            tmpListLayer.pop_back();
        }
    }
    while(!tmpListLayer.empty()){
        tmpLayer = tmpListLayer.back();
        tmpListLayer.pop_back();
        listLayers.push_back(tmpLayer);
    }
    updateProprietyLayer();
    update();
}

void DrawingArea::takeUpTheLayer(){
    list <Layer*> tmpListLayer;
    tmpListLayer.clear();
    Layer* tmpLayer;
    Layer* tmpNextLayer;
    // We pop the layer of the list of layer to find the current layer
    if(!listLayers.empty()){
        tmpLayer = listLayers.back();
        while(tmpLayer != currentLayer && !listLayers.empty())        {
            tmpListLayer.push_back(tmpLayer);
            listLayers.pop_back();
            tmpLayer = listLayers.back();
        }
        if(!listLayers.empty())        {
            // We pop the current layer of the listLayers
            listLayers.pop_back();
            if(!listLayers.empty()){
                // Recuperation of the next layer
                tmpNextLayer = listLayers.back();
                listLayers.pop_back();
                qreal zValueTmpLayer = tmpLayer->zValue();
                // Interchangement of the zvalue of the current layer and its previous layer
                tmpLayer->setZValue(tmpNextLayer->zValue());
                tmpNextLayer->setZValue(zValueTmpLayer);
                // Insertion of the previous layer and the current layer
                listLayers.push_back(tmpLayer);
                listLayers.push_back(tmpNextLayer);
            }
        }
    }
    while(!tmpListLayer.empty())    {
        tmpLayer = tmpListLayer.back();
        tmpListLayer.pop_back();
        listLayers.push_back(tmpLayer);
    }
    updateProprietyLayer();
    update();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fusion

void DrawingArea::fusionVisibleLayers(e_fusion typeFusion){
    list <Layer*> listLayerToFusion;
    listLayerToFusion.clear();
    list <Layer*> tmpListLayer = getLayers();
    if(getLayersCount() <= 1){
        return;
    }
    // Creation of the list of layer to fusion
    while(!tmpListLayer.empty())    {
        Layer* tmpLayer = tmpListLayer.back();
        if(tmpLayer->isVisible())        {
            listLayerToFusion.push_front(tmpLayer);
        }
        tmpListLayer.pop_back();
    }
    list <Layer*> listLayerToFusionCopy = listLayerToFusion;
    FusionLayers fusion(typeFusion, width, height);
    // Creation of the new layer representing the fusion of the other layer
    Layer* layerFusion = (fusion.fusion(listLayerToFusion));
    // Push the layer in the list of layers of the drawing
    list<Layer*> tmpContainerListLayer;
    tmpContainerListLayer.clear();
    while(listLayerToFusionCopy.back() != listLayers.back())    {
        tmpContainerListLayer.push_back(listLayers.back());
        listLayers.pop_back();
    }
    listLayers.push_back(layerFusion);
    while(!tmpContainerListLayer.empty())    {
        listLayers.push_back(tmpContainerListLayer.back());
        tmpContainerListLayer.pop_back();
    }
    list <Layer*> tmpListLayers;
    tmpListLayers.clear();
    // Elimination of the old layers that have been fusionned
    while(!listLayerToFusionCopy.empty()){
        Layer* tmpLayerFusion = listLayerToFusionCopy.back();
        listLayerToFusionCopy.pop_back();
        int numLayerList = listLayers.size();
        for(int i = 0; i < numLayerList; i++){
            Layer* layerListDrawing = listLayers.back();
            listLayers.pop_back();
            if(tmpLayerFusion == layerListDrawing){
                scene()->removeItem(tmpLayerFusion);
                delete tmpLayerFusion;
            }else{
                 listLayers.push_front(layerListDrawing);
            }
        }
    }
    scene()->addItem(layerFusion);
    setCurrentLayer(layerFusion);   
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Update

void DrawingArea::update(){
    semaphore->acquire(1);
    viewport()->update();

    semaphore->release(1);

}

void DrawingArea::updateProprietyLayer(){
    pimage->updateCalque(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set

void DrawingArea::setCurrentLayer(Layer* layer){
    currentLayer = layer;
    borderLayer->setLayer(currentLayer);
    update();
    updateProprietyLayer();
}

void DrawingArea::setWidth(int widthP){
    width = widthP;
}

void DrawingArea::setHeight(int heightP){
    height = heightP;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Get

list <Layer*> DrawingArea::getLayers(){
    list <Layer*> tmpListLayer = listLayers;
    if(tmpListLayer.size() >= 2){
        pimage->ui->actionRemoveLayer->setEnabled(true);
    }else{
        pimage->ui->actionRemoveLayer->setEnabled(false);
    }
    return tmpListLayer;
}

int DrawingArea::getWidth(){
    return width;
}

int DrawingArea::getHeight(){
    return height;
}

int DrawingArea::getMargeSceneX(){
    return margeSceneX;
}

int DrawingArea::getMargeSceneY(){
    return margeSceneY;
}

std::list <Layer*> DrawingArea::getListLayers(){
    if(listLayers.size() >= 2){
        pimage->ui->actionRemoveLayer->setEnabled(true);
    }else{
        pimage->ui->actionRemoveLayer->setEnabled(false);
    }
    return listLayers;
}

int DrawingArea::getLayersCount(){
    return listLayers.size();
}

Layer* DrawingArea::getCurrentLayer(){
    return currentLayer;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Zoom

void DrawingArea::scale( qreal sx, qreal sy ){
    scaleXY = scaleXY * sx;
    QGraphicsView::scale(sx, sy);
}

qreal DrawingArea::getScale(){
    return scaleXY;
}


